﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using VRacer.Packets;
using VRacer.Exceptions;
using System.Threading;

namespace VRacer.Connection
{
    /// <summary>
    /// Obsluguje polaczenie sieciowe TCP z serwerem. Z pomoca <c>Sender</c>a i <c>Receiver</c>a wysyla i odbiera pakiety.
    /// Dostarcza takze dostep do gniazda (Socket) i informacje czy polaczenie jest aktywne, oraz oczywiscie metody Connect i Disconnect.
    /// </summary>
    public class ClientConnection : ConnectionBase, IDisposable
    {
        public event EventHandler Connected;
        public event EventHandler<ErrorEventArgs> Error;
        public event EventHandler Disconnected;

        private bool _isConnecting = false;
        private bool _isActive = false;
        private string _hostname;
        private int _port;
        private TcpClient _client;
        private Sender _sender;
        private Receiver _receiver;
        private bool _keepAlive = true;

        public ClientConnection()
        {
            this.Sender = new Sender(this);
            this.Receiver = new Receiver(this);
            _client = new TcpClient();
        }

        public ClientConnection(TcpClient tcpClient)
            : this()
        {
            _client = tcpClient;
            _isActive = true;
            OnConnected();
        }

        public ClientConnection(string hostname, int port)
            : this()
        {
            _hostname = hostname;
            _port = port;
            _client = new TcpClient();

            _client.SendBufferSize = 1024;
            _client.Client.NoDelay = true;
            _client.Client.UseOnlyOverlappedIO = true;
        }

        /// <summary>
        /// Determinuje czy podtrzymywac polaczenie (Ping)
        /// </summary>
        /// <value><c>true</c> if [keep alive]; otherwise, <c>false</c>.</value>
        public bool KeepAlive
        {
            get { return _keepAlive; }
            set { _keepAlive = value; }
        }




        public TcpClient Socket
        {
            get { return _client; }
        }

        /// <summary>
        /// Gets the connection stream.
        /// </summary>
        /// <value>The connection stream.</value>
        public System.IO.Stream Stream
        {
            get { return _client.GetStream(); }
        }

        /// <summary>
        /// Obsługuje odbieranie pakietów
        /// </summary>
        /// <value>The packet receiver.</value>
        protected Receiver Receiver
        {
            get { return _receiver; }
            set { _receiver = value; }
        }

        /// <summary>
        /// Obsługuje wysyłanie pakietów
        /// </summary>
        /// <value>The sender.</value>
        protected Sender Sender
        {
            get { return _sender; }
            set { _sender = value; }
        }

        /// <summary>
        /// Connects this instance to the game server.
        /// </summary>
        public override void Connect()
        {
            Connect(_hostname, _port);
        }

        public void Connect(string hostname, int port)
        {
            if (!_isConnecting)
            {
                _isConnecting = true;
                _client.BeginConnect(hostname, port, new AsyncCallback(clientConnected), null);
            }
        }

        internal void Disconnect(Exception e)
        {
            if (e is UIException)
                OnError(e);
            else
                OnError(new Exceptions.RemoteGameConnectionLostException(e));

            Disconnect();
        }

        /// <summary>
        /// Disconnects this instance from game server.
        /// </summary>
        public override void Disconnect()
        {
            _isActive = false;
            _client.Client.Close();
            _client.Close();
            OnDisconnected();
        }

        #region IDisposable Members

        public void Dispose()
        {
            Sender.Dispose();
            Receiver.Dispose();
        }

        #endregion


        private void clientConnected(IAsyncResult ar)
        {
            try
            {
                _isConnecting = false;
                if (_client.Connected)
                {
                    Thread.Sleep(2000);
                    IsActive = true;
                    OnConnected();
                }
                else
                {
                    OnError(new Exceptions.RemoteGameConnectionException(null));
                }
            }
            catch (Exception e)
            {
                OnError(new Exceptions.RemoteGameConnectionException(e));
            }
        }

        
        protected virtual void OnConnected()
        {
            if (Connected != null)
                Connected(this, EventArgs.Empty);
        }

        private void OnError(Exception e)
        {
            if (Error != null)
                Error(this, new ErrorEventArgs(e));
        }

        protected virtual void OnDisconnected()
        {
            if (Disconnected != null)
                Disconnected(this, EventArgs.Empty);
        }

        /// <summary>
        /// Gets or sets a value indicating whether this connection is connected.
        /// </summary>
        /// <value><c>true</c> if this instance is connected; otherwise, <c>false</c>.</value>
        public override bool IsActive
        {
            get { return _isActive; }
            set { _isActive = true; }
        }

        #region IPacketReceiver Members

        public event PacketReceivedHandler PacketReceived
        {
            add { Receiver.PacketReceived += value; }
            remove { Receiver.PacketReceived -= value; }
        }

        #endregion

        #region IPacketSender Members

        public event PacketSentHandler PacketSent
        {
            add { Sender.PacketSent += value; }
            remove { Sender.PacketSent -= value; }
        }

        public void Send(PacketBase packet)
        {
            Sender.Send(packet);
        }

        #endregion
    }
}
